#ifndef __CHANNEL_HPP__
#define __CHANNEL_HPP__

/* Channle模块：对文件描述符上的 事件监控 进行管理。
    1. 添加——在文件描述符上监控新的事件
    2. 修改——在文件描述符上修改已监控的事件，不再监控已监控的事件
    3. 删除——删除文件描述符上的所有已监控的事件，取消监控该文件描述符
    4. 为指定事件设置被触发后的回调函数，包括可读事件、可写事件、错误事件、套接字关闭事件、任意事件

    一个Channel管理一个文件描述符。
 */
// class Poller;// 要用到，所以要提前声明
class EventLoop;

class Channel
{
    /* 回调函数类型定义 */
    using ReadCallback = std::function<void()>;     // 可读事件回调函数定义
    using WriteCallback = std::function<void()>;    // 可写事件回调函数定义
    using ClosedCallback = std::function<void()>;   // 连接关闭事件回调函数定义
    using ErrorCallback = std::function<void()>;    // 错误事件回调函数定义
    using EventCallback = std::function<void()>;    // 任意事件回调函数定义
private:
    int _sockfd;                    // 要监控的文件描述符
    uint32_t _events;               // 要监控的事件 集
    uint32_t _revents;              // 已监控的事件中被触发的事件 集
    // Poller* _poller;             // Poller模块的操作句柄，对事件监控的实际更新操作，都在Poller模块中完成
    EventLoop* _eventloop;          // 该文件描述符绑定的事件循环

    ReadCallback _read_cb;          // 可读事件被触发后的回调函数
    WriteCallback _write_cb;        // 可写事件被出发后的回调函数
    ClosedCallback _closed_cb;      // 套接字被关闭事件被触发的回调函数
    ErrorCallback _error_cb;        // 错误事件被触发后的回调函数
    EventCallback _event_cb;        // 任意事件被触发后的回调函数
public:
    // Channel(int sockfd, Poller* poller): _sockfd(sockfd), _events(0), _revents(0), _poller(poller)
    // {}
    /**
     * @brief 构造函数
     * 
     * @param sockfd 封装的文件描述符
     * @param eventloop 文件描述符所在的事件循环
     */
    Channel(int sockfd, EventLoop* eventloop): _sockfd(sockfd), _events(0), _revents(0), _eventloop(eventloop)
    {}
    /**
     * @brief 析构函数
     * 
     */
    ~Channel()
    {
        _sockfd = -1;
        _events = _revents = 0;
        _eventloop = nullptr;
        _read_cb = _write_cb = _closed_cb = _error_cb = _event_cb = nullptr;
    }

    /* 设置回调函数 */
    /**
     * @brief 设置/注册 可读事件被触发的回调函数
     * 
     * @param cb 
     */
    void SetReadCallback(const ReadCallback& cb)
    {
        _read_cb = cb;
    }
    /**
     * @brief 设置/注册 可写事件被触发的回调函数
     * 
     * @param cb 
     */
    void SetWriteCallback(const WriteCallback& cb)
    {
        _write_cb = cb;
    }
    /**
     * @brief 设置/注册 连接关闭事件被触发的回调函数
     * 
     * @param cb 
     */
    void SetClosedCallback(const ClosedCallback& cb)
    {
        _closed_cb = cb;
    }
    /**
     * @brief 设置/注册 错误事件被触发的回调函数
     * 
     * @param cb 
     */
    void SetErrorCallback(const ErrorCallback& cb)
    {
        _error_cb = cb;
    }
    /**
     * @brief 设置/注册 任意事件被触发的回调函数
     * 
     * @param cb 
     */
    void SetEventCallback(const EventCallback& cb)
    {
        _event_cb = cb;
    }

    /**
     * @brief 获取监控的文件描述符（用于epoll获取要监控的文件描述符）
     * 
     * @return int 文件描述符
     */
    int Fd() const
    {
        return _sockfd;
    }
    /**
     * @brief 获取要监控的事件（用于epoll获取要监控的事件）
     * 
     * @return uint32_t 
     */
    uint32_t Events() const
    {
        return _events;
    }
    /**
     * @brief 设置/注册 被触发的事件
     * 
     * @param events 被触发的事件
     */
    void SetREvents(const uint32_t events)
    {
        // DBG_LOG("Channel:SetREvents");
        _revents = events;
    }

    /* 启动对读事件的监控 */
    void EnableRead()
    {
        // DBG_LOG("Channel:EnableRead");
        _events |= EPOLLIN;
        UpdateEvents();
    }
    /* 启动对写事件的监控 */
    void EnableWrite()
    {
        // DBG_LOG("Channel:EnableWrite");
        _events |= EPOLLOUT;
        UpdateEvents();
    }
    /* 取消对读事件的监控 */
    void DisableRead()
    {
        // DBG_LOG("Channel:DisableRead");
        _events &= ~EPOLLIN;
        UpdateEvents();
    }
    /* 取消对写事件的监控 */
    void DisableWrite()
    {
        // DBG_LOG("Channel:DisableWrite");
        _events &= ~EPOLLOUT;
        UpdateEvents();
    }
    /* 取消对当前文件描述符的监控——取消文件按描述符上的所有监控事件 */
    void DisableAll()
    {
        // DBG_LOG("Channel:RemoveAllEvents");
        _events = 0;
        UpdateEvents();
    }
    /* 是否监控了可读事件 */
    bool MonitorRead() const
    {
        return _events & EPOLLIN;
    }
    /* 是否监控了可写事件 */
    bool MonitorWrite() const
    {
        return _events & EPOLLOUT;
    }

    /* 更新文件描述符上的 事件监控 —— 没有对该文件描述符监控就添加监控 */
    void UpdateEvents();
    /* 取消对当前文件描述符上的所有事件的监控 */
    void RemoveEvents();

    /* 处理事件 */
    void HandleEvent()
    {
        /* 总是被调用 */
        if(_revents & (EPOLLIN | EPOLLPRI | EPOLLRDHUP))
        {
            if(_event_cb) _event_cb();
            if(_read_cb) _read_cb();
        }

        /* 可能被调用 */
        if(_revents & EPOLLOUT)
        {
            if(_event_cb)
            {
                // DBG_LOG("WriteCallback:EventCallback, sockfd: %d", _sockfd);
                _event_cb();// 只有这里有bug，已解决：析构函数显式重置成员变量
                // DBG_LOG("EventCallback done");
            }
            if(_write_cb) _write_cb();
            // DBG_LOG("WriteCallback done");
            
        }
        else if(_revents & EPOLLERR)
        {
            if(_event_cb) _event_cb();
            if(_error_cb) _error_cb();
        }
        else if(_revents & EPOLLHUP)
        {
            if(_event_cb) _event_cb();
            if(_closed_cb) _closed_cb();
        }

        // if(_event_cb) _event_cb();// 不方便定位错误，是哪个事件触发的，所以将其移动到事件触发那里
    }
};

#endif